package com.windward.vii.service.weixin;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.sun.jersey.api.client.ClientResponse;
import com.windward.vii.common.JsonResult;
import com.windward.vii.common.ResultCode;
import com.windward.vii.config.WeixinPayConfig;
import com.windward.vii.dao.entity.Order;
import com.windward.vii.service.AssetUserAccountService;
import com.windward.vii.service.OrderService;
import com.windward.vii.service.weixin.bean.OrderRequest;
import com.windward.vii.service.weixin.bean.OrderResponse;
import com.windward.vii.utils.HttpRestClient;
import com.windward.vii.utils.StringUtils;
import com.windward.vii.utils.encrypt.MD5Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

@Component
public class WeixinPayService {

    public static final int TIMEOUT = 2000;
    public static final String ENCODE_UTF8 = "utf-8";
    public static final String SUCCESS = "SUCCESS";
    public static final String FEE_TYPE_CNY = "CNY";
    public static final String FAIL = "FAIL";
    public static final Integer EFFECT_TIME_MINUTES = 10;
    public static final String URL_NOTIFY = "/v1/api0/wxpay/callback";
    public static final String URL_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    public static final String URL_ORDER_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery";

    private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
    private static Logger logger = LoggerFactory.getLogger(WeixinPayService.class);

    @Resource
    private WeixinPayConfig weixinPayConfig;
    @Resource
    private OrderService payOrderService;
    @Resource
    private AssetUserAccountService accountService;

    public JsonResult unifiedOrder(OrderRequest orderRequest) {
        return postRequest(orderRequest, URL_ORDER);
    }

    public JsonResult queryOrder(OrderRequest orderRequest) {
        return postRequest(orderRequest, URL_ORDER_QUERY);
    }

    /**
     * 请求微信接口
     *
     * @param orderRequest
     * @return
     * @url 请求url
     */
    private JsonResult postRequest(OrderRequest orderRequest, String url) {
        logger.info("微信API请求开始：url={}，currentTime={}", url, System.currentTimeMillis());
        JsonResult jsonResult = JsonResult.error();
        try {
            XmlMapper xmlMapper = new XmlMapper();
            xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            String xmlRequest = xmlMapper.writeValueAsString(orderRequest);
            ClientResponse response = HttpRestClient.post(url, xmlRequest);
            if(response == null) {
                return new JsonResult(ResultCode.ERROR_CODE, "微信API请求失败(response is null)，请重试");
            }
            String content = response.getEntity(String.class);
            if(StringUtils.isBlank(content)) {
                return new JsonResult(ResultCode.ERROR_CODE, "微信API请求失败(content is blank)，请重试");
            }
            XmlMapper responseXmlMapper = new XmlMapper();
            OrderResponse orderResponse = responseXmlMapper.readValue(content, OrderResponse.class);
            logger.info("微信API请求信息：postRequest={}，xmlRequest={}，response={}", orderRequest.toString(), xmlRequest, content);
            jsonResult = validateResponse(content, orderResponse);
            if (jsonResult.getCode() == ResultCode.SUCCESS_CODE) {
                jsonResult.setBody(orderResponse);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("微信API请求异常：postRequest={}，exception={}", orderRequest, e.getMessage());
            jsonResult.setMsg(e.getMessage());
        }
        logger.info("微信API请求结束：订单sn={}，currentTime={}", orderRequest.getOut_trade_no(), System.currentTimeMillis());
        return jsonResult;
    }

    public OrderRequest createOrderRequest(Order order){
        OrderRequest orderRequest = new OrderRequest();
        orderRequest.setAppid(weixinPayConfig.getAppid());
        orderRequest.setMch_id(weixinPayConfig.getMchid());
        orderRequest.setDevice_info("WEB");
        orderRequest.setNonce_str(getNonceStr());
        orderRequest.setBody("VII-" + order.getGoodsName());
        // 要求32个字符
        if(StringUtils.isNotEmpty(order.getSn())){
            orderRequest.setOut_trade_no(order.getSn());
        }
        orderRequest.setFee_type(FEE_TYPE_CNY);
        orderRequest.setTotal_fee(Math.toIntExact(order.getAmount() * 100)); // 分
        if(StringUtils.isNotEmpty(order.getIp())){
            orderRequest.setSpbill_create_ip(order.getIp());
        }
        orderRequest.setTime_start(format.format(order.getCreateTime()));
        orderRequest.setTime_expire(format.format(order.getExpireTime()));
//        if(StringUtils.isNotEmpty(payOrder.getProductTag())){
//            orderRequest.setGoods_tag(payOrder.getProductTag());
//        }
        orderRequest.setNotify_url("http://"+ weixinPayConfig.getCallBackIp()+URL_NOTIFY);
//        if(payOrder.getId() > 0){
//            orderRequest.setProduct_id(String.valueOf(payOrder.getId()));
//        }
        orderRequest.setSign(generateSign(orderRequest));
        logger.info("充值发送数据，orderRequest={}",orderRequest.toString());
        return orderRequest;
    }

    public OrderRequest createOrderRequest(String sn) {
        OrderRequest orderRequest = new OrderRequest();
        orderRequest.setAppid(weixinPayConfig.getAppid());
        orderRequest.setMch_id(weixinPayConfig.getMchid());
        orderRequest.setOut_trade_no(sn);
        orderRequest.setNonce_str(getNonceStr());
        orderRequest.setSign(generateSign(orderRequest));
        return orderRequest;
    }

    /******************************************************/

    /**
     * 验证返回结果
     *
     * @param xmlRes
     * @param orderResponse
     * @return
     */
    private JsonResult validateResponse(String xmlRes, OrderResponse orderResponse) {
        JsonResult jsonResult = JsonResult.success();
        if (StringUtils.isEmpty(xmlRes)) {
            logger.info("接口返回结果为空，sn={}", orderResponse.getOut_trade_no());
            jsonResult = JsonResult.error("接口返回结果为空");
        } else if (FAIL.equals(orderResponse.getReturn_code())) {
            //通讯错误
            logger.info("通讯错误，sn={}", orderResponse.getOut_trade_no());
            jsonResult = JsonResult.error(orderResponse.getReturn_msg());
        } else if (FAIL.equals(orderResponse.getResult_code())) {
            //业务错误
            logger.info("业务错误，sn={}", orderResponse.getOut_trade_no());
            jsonResult = JsonResult.error(orderResponse.getErr_code_des());
        } else if (!verifySign(orderResponse)) {
            logger.info("验证签名，sn={}", orderResponse.getOut_trade_no());
            return JsonResult.error("sign error");
        }
        return jsonResult;
    }

    /**
     * 验证签名
     *
     * @param orderResponse
     * @return
     */
    public boolean verifySign(OrderResponse orderResponse) {
        String signGen = generateSign(orderResponse);
        if (!orderResponse.getSign().equals(signGen)) {
            return false;
        }
        return true;
    }



    /**
     * 生成随机字符串
     *
     * @return
     */
    private String getNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 将参数排序
     *
     * @param object
     * @return
     */
    private TreeMap<String, String> getSortMap(Object object) {
        TreeMap<String, String> map = new TreeMap<String, String>();
        Field fields[] = object.getClass().getDeclaredFields();
        String[] name = new String[fields.length];
        Object[] value = new Object[fields.length];
        try {
            Field.setAccessible(fields, true);
            for (int i = 0; i < name.length; i++) {
                name[i] = fields[i].getName();
                value[i] = fields[i].get(object);
                if (value[i] != null) {
                    map.put(name[i], value[i].toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    public String generateSign(Object object) {
        return generateSign(getSortMap(object));
    }

    private String generateSign(TreeMap<String, String> map) {
        StringBuilder builder = new StringBuilder();
        Iterator it = (Iterator) map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (key.equals("sign")) {
                continue;
            }
            if (StringUtils.isNotEmpty(value)) {
                builder.append(key).append("=").append(value.trim()).append("&");
            }
        }
        if (builder.length() > 1) {
            builder.append("key=").append(weixinPayConfig.getApiKey());
        }
        logger.info("before md5=" + builder.toString());
        return MD5Util.md5Hex(builder.toString()).toUpperCase();
    }

}
